﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using Development.Tools;
using System.Net;
using System.Threading;
using Development.Network.Msg;

namespace Development.Network.AsycnSocket
{
    /// <summary>
    /// 异步发送客户端
    /// </summary>
    public class AsycnClient
    {
        protected SocketAsyncEventArgs m_receiveEventArgs;   //接受异步
        protected SocketAsyncEventArgs m_sendEventArgs;     //发送异步
        protected DateTime m_ConnectDateTime;              //连接的时间
        protected DateTime m_ActiveDateTime;                //激活时间

        private string m_host;
        private int m_port;


        private System.Timers.Timer m_timer;


        protected byte[] m_recvBuffer;                //接受缓存

        protected DynamicBuffer m_recvSplitBuffer;

        protected DynamicBuffer m_sendBuffer;

        protected NetworkStream m_sendStream;

        protected bool m_asynSend = false;

        public DynamicBuffer RecvSplietBuffer { get { return m_recvSplitBuffer; } }
        protected Socket m_connectSocket;           //连接socket

        public delegate void ReconnectHandle(AsycnClient client);
        public event ReconnectHandle ReconnectEvent;    //重连事件

        public delegate void CloseClientHandle(AsycnClient client);
        public event CloseClientHandle CloseClientEvent;    //关闭事件
        public bool IsConnected;



        public AsycnClient(int bufferSize)
        {
            m_recvBuffer = new byte[bufferSize];
            m_recvSplitBuffer = new DynamicBuffer(bufferSize);
            m_sendBuffer = new DynamicBuffer(bufferSize);
            IsConnected = false;
            m_asynSend = false;
            m_receiveEventArgs = new SocketAsyncEventArgs();
            m_sendEventArgs = new SocketAsyncEventArgs();
            m_receiveEventArgs.SetBuffer(m_recvBuffer, 0, bufferSize);
            m_receiveEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
            m_sendEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
            StartHeartJump();

        }



        void StartHeartJump()
        {
            CloseHeartJump();
            m_timer = new System.Timers.Timer(5000);
            m_timer.Elapsed += m_timer_Elapsed;

        }

        void m_timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Msg.MsgBase msg = new Msg.MsgBase();
            msg._MsgId = 10;
            SendMsg(msg.Pack());
        }

        void CloseHeartJump()
        {
            if (m_timer != null)
            {
                m_timer.Stop();
            }
        }

        void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            m_ActiveDateTime = DateTime.Now;
            try
            {
                lock (this)
                {
                    if (e.LastOperation == SocketAsyncOperation.Receive)
                        OnReceive(e);
                    else if (e.LastOperation == SocketAsyncOperation.Send)
                        OnSend(e);
                    else if (e.LastOperation == SocketAsyncOperation.Connect)
                        OnConnect(e);
                    else
                        throw new ArgumentException("The last operation completed on the socket was not a receive or send");
                }
            }
            catch (Exception E)
            {
                Log.WriteLog(ELogLevel.LOG_ERROR, string.Format("IO_Completed {0} error, message: {1}", m_connectSocket, E.Message));
            }
        }

        protected void OnConnect(SocketAsyncEventArgs args)
        {
            if (args.SocketError != SocketError.Success)
            {
                IsConnected = false;
                CloseClient();
                Log.WriteLog(ELogLevel.LOG_INFO, string.Format("connect fail!host:{0},port:{1},reconnect", m_host, m_port));
                Reconnect();
            }
            else
            {
                m_timer.Start();
                IsConnected = true;
                PostReceive();
                Log.WriteLog(ELogLevel.LOG_INFO, string.Format("connect success!host:{0},port:{1}", m_host, m_port));
            }
        }

        protected virtual void OnReceive(SocketAsyncEventArgs args)
        {
            if (args.BytesTransferred > 0 && args.SocketError == SocketError.Success)
            {
                m_recvSplitBuffer.WriteBuffer(m_recvBuffer, 0, args.BytesTransferred);
                //开始解包
                //前四个字节为包的长度，后面的内容是协议数据
                while (m_recvSplitBuffer.DataCount >= 4)
                {
                    int packetLength = BitConverter.ToInt32(m_recvSplitBuffer.Buffer, 0);
                    if ((packetLength <= 0) || (packetLength > 10 * 1024 * 1024) || (m_recvSplitBuffer.DataCount > 10 * 1024 * 1024))
                    {
                        //异常包
                        CloseClient();
                        return;
                    }

                    //消息报长度不够，等待下一次处理
                    if (m_recvSplitBuffer.DataCount < packetLength)
                    {
                        break;
                    }
                    else
                    {
                        //解析包
                        MsgPacket msg = MsgPacket.SplitPacket(this,m_recvSplitBuffer.Buffer, 0, packetLength);
                        Routiner.Instance.AddMsgPacket(msg);
                        //处理完消息包，重置缓存
                        m_recvSplitBuffer.Clear(packetLength);
                    }

                }
                //继续投递接收消息
                PostReceive();
            }
            else
            {
                CloseClient();
            }
        }
        protected void OnSend(SocketAsyncEventArgs args)
        {
            if (args.BytesTransferred != 0 && args.SocketError == SocketError.Success)
            {
                m_sendBuffer.Clear(args.BytesTransferred);
                if (m_sendBuffer.DataCount > 0)
                {
                    m_sendEventArgs.SetBuffer(m_sendBuffer.Buffer, 0, m_sendBuffer.DataCount);
                    bool willRaiseEvent = m_connectSocket.SendAsync(m_sendEventArgs);
                    if (!willRaiseEvent)
                    {
                        OnSend(m_sendEventArgs);
                    }
                }
                else
                {
                    m_asynSend = false;
                }
            }
            else
            {
                CloseClient();
            }

        }

        protected void PostSend()
        {
            if (m_asynSend)
            {
                return;
            }
            if (m_sendBuffer.DataCount > 0)
            {
                m_asynSend = true;
                m_sendEventArgs.SetBuffer(m_sendBuffer.Buffer, 0, m_sendBuffer.DataCount);
                bool willRaiseEvent = m_connectSocket.SendAsync(m_sendEventArgs);
                if (!willRaiseEvent)
                {
                    OnSend(m_sendEventArgs);
                }
            }
        }
        protected void PostReceive()
        {
            bool willRaiseEvent = m_connectSocket.ReceiveAsync(m_receiveEventArgs);
            if (!willRaiseEvent)
            {
                OnReceive(m_receiveEventArgs);
            }
        }

        public void Connect(string url,bool isWait = false)
        {
            string[] temp = url.Split(':');
            Connect(temp[0], int.Parse(temp[1]), isWait);
        }
        /// <summary>
        /// 开始连接
        /// </summary>
        /// <param name="host"></param>
        /// <param name="port"></param>
        public void Connect(string host, int port, bool isWait = false)
        {
            m_host = host;
            m_port = port;
            IsConnected = false;
            if (m_connectSocket == null)
            {
                m_connectSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            }
            SocketAsyncEventArgs connectEventArgs = new SocketAsyncEventArgs();
            connectEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
            connectEventArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(host), port);
            bool willRaiseEvent = m_connectSocket.ConnectAsync(connectEventArgs);
            if (!willRaiseEvent)
            {
                OnConnect(connectEventArgs);
            }
            if(isWait)
            {
                ThreadUtils.WaitTrue(ref IsConnected,new TimeSpan(0,0,0,20,0));
            }
        }


        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer"></param>
        public void SendMsg(byte[] buffer)
        {
            lock (this)
            {
                if (m_connectSocket == null)
                {
                    return;
                }
                m_sendBuffer.WriteBuffer(buffer);
                PostSend();
            }

        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer"></param>
        public void SendMsg(byte[] buffer, int offset, int count)
        {
            lock (this)
            {
                if (m_connectSocket == null)
                {
                    return;
                }
                m_sendBuffer.WriteBuffer(buffer, offset, count);
                PostSend();
            }
        }


        /// <summary>
        /// 重连
        /// </summary>
        public void Reconnect()
        {
            if (ReconnectEvent != null)
            {
                ReconnectEvent(this);
            }
            lock (this)
            {
                CloseClient();
            }
            Connect(m_host, m_port);
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        public void CloseClient()
        {
            IsConnected = false;
            CloseHeartJump();
            if (m_connectSocket != null)
            {
                Log.WriteLog(ELogLevel.LOG_INFO, "client disconnect");
                try
                {
                    m_connectSocket.Shutdown(SocketShutdown.Both);
                    m_connectSocket.Close();
                    m_connectSocket = null;
                }
                catch (Exception) { };
                if (CloseClientEvent != null)
                {
                    CloseClientEvent(this);
                }

                m_recvSplitBuffer.Clear();
                m_sendBuffer.Clear();
            }
        }


    }
}
